/**
 * 
 */
package netPortfolioValue;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
 * @author mingfan
 *
 * Represent a portfolio of stocks
 */
public class Portfolio {
	
	// The file which contains tickers of all the stocks in
	// this portfolio
	private String tickerFile = "stocks.txt";
	
	// A Map contains ticker name -- holding shares number
	private final Map<String, Integer> portfolio = new HashMap<String, Integer>();
	
	// Coefficient indicate how much time will be spent on IO. Used to determine number of threads
	private final double BLOCK_COEFFICIENT = 0.9; 

	/**
	 * Constructor
	 * Load ticker -- holding number file to contstruct object of this class
	 */
	public Portfolio () {
		
		try (BufferedReader in = new BufferedReader(new FileReader(tickerFile))) {
			String oneLine;
			while ((oneLine = in.readLine()) != null) {
				final String[] items = oneLine.split(",");
				portfolio.put(items[0], Integer.valueOf(items[1]));
			}
			
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			System.exit(-1);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			System.exit(-1);
		}
		
	}
	
	/**
	 * Calculate total value of this portfolio serially
	 * 
	 * @return
	 * @throws IOException 
	 */
	public double getPortfolioValueSerial () throws IOException {
		
		double returnValue = 0;
		
		for (String ticker : portfolio.keySet()) {
			returnValue += YahooFinance.getPriceQuotation(ticker).getClosePrice() * portfolio.get(ticker);
		}
		
		return returnValue;
	}
	
	/**
	 * Calculate total value of this portfolio concurrently
	 * 
	 * @return
	 * @throws InterruptedException 
	 * @throws ExecutionException 
	 */
	public double getPortfolioValueConcurrent () throws InterruptedException, ExecutionException {
		
		final int processorNumber = Runtime.getRuntime().availableProcessors();
		final int poolSize = (int)(processorNumber/(1-BLOCK_COEFFICIENT));
		System.out.println("Number of processors: " + processorNumber);
		System.out.println("Number of concurrent thread pool size: " + poolSize);
		
		// Prepare tasks for thread pool
		final List<Callable<Double>> taskDivsion = new ArrayList<Callable<Double>>();
		for (final String ticker : portfolio.keySet()) {
			taskDivsion.add(new Callable<Double>() {

				@Override
				public Double call() throws Exception {
					// TODO Auto-generated method stub
					return new Double(portfolio.get(ticker) * YahooFinance.getPriceQuotation(ticker).getAdjustClosePrice());
				}
			});
		}
		
		// Initialize thread pool
		final ExecutorService threadPool = Executors.newFixedThreadPool(poolSize);
		
		// Submit bunch of tasks
		final List<Future<Double>> partialResults = threadPool.invokeAll(taskDivsion, 1000, TimeUnit.SECONDS);
		
		// Collect all results and reach the sum up of portfolio's total value
		double returnValue = 0;
		for (Future<Double> f : partialResults) {
			returnValue += f.get();
		}
		
		//
		threadPool.shutdown();
		
		return returnValue;
	}
	
	/*--------------------------------------------------*/
	
	/**
	 * For test purpose
	 * 
	 * @return the portfolio
	 */
	public Map<String, Integer> getPortfolio() {
		return portfolio;
	}


	/**
	 * Used just for test
	 * 
	 * @param args
	 * @throws IOException 
	 * @throws ExecutionException 
	 * @throws InterruptedException 
	 */
	public static void main(String[] args) throws IOException, InterruptedException, ExecutionException {
		// TODO Auto-generated method stub
		Portfolio p = new Portfolio();

		System.out.println("start seriall way of calculation....");
		long start = System.nanoTime();
		System.out.printf("Total value of portfolio is: %f%n", p.getPortfolioValueSerial());
		long end = System.nanoTime();
		System.out.println("Time uses:" + (end-start)/1.0e9);
		
		System.out.println("start concurrent way of calculation...");
		start = System.nanoTime();
		System.out.printf("Total value of portfolio is: %f%n", p.getPortfolioValueConcurrent());
		end = System.nanoTime();
		System.out.println("Time uses:" + (end - start)/1.0e9);
	}

}
